Skip to main content
  1. posts/

How To Create Custom Taxonomies and a Dropdown Menu in Hugo with Congo Theme

When I started this blog, one of the first things I wanted was flexible tagging — the ability to group posts not just by categories but also by project, theme, or technology. And since I’m using Hugo with the Congo theme, I had a solid foundation to build on.

Menu with submenu items

In this post, I’ll walk you through:

  • How I defined custom taxonomies
  • How I structured content around them
  • How I created a menu with dropdown subitems
  • And the changes I had to make to Congo’s templates and CSS to support this

Let’s get into it.


Defining Custom Taxonomies in Hugo #

Out of the box, Hugo supports taxonomies like tags and categories, but you can define as many custom ones as you like.

To do that, I created a file at:

config/_default/taxonomies.toml

And added the following:

categories = "categories" 
myprojects = "my-projects" 
themes = "themes" 
technology = "technologies"

This allows you to tag your blog posts using any of these taxonomies in the Front Matter, like so:

categories = ["programming"] 
my-projects = ["oktan"] 
themes = ["lessons-learned"] 
technology = ["cplusplus", "sqlite"]

Organizing Content #

To support the my-projects taxonomy, I created a structure like this in the content directory:

content/
├── my-projects/
│   ├── oktan/
│   │   ├── index.md
│   │   ├── oktan-screenshot.png
│   │   └── thumbnail.webp
│   └── _index.md
├── themes/
├── technologies/
└── categories/

Now, any post tagged with my-projects = ["oktan"] will automatically get linked to the /my-projects/oktan/ page.


Creating a Menu with Submenus #

The next challenge was navigation. I wanted to show a top-level menu (Blog) with submenu items like Programming, Project Management, etc.

Hugo makes it easy to define menu items in your config:

# config/_default/menus.en.toml

[[main]]
  identifier = "blog"
  name = "Blog"
  pageRef = "posts"
  weight = 10

[[main]]
  parent = "blog"
  name = "Programming"
  pageRef = "programming"
  weight = 11

[[main]]
  parent = "blog"
  name = "Project Management"
  pageRef = "project-management"
  weight = 12
  
  ...

The key here is the parent = "blog" line — it lets Hugo nest these items and expose them via .Children in the templates.


Modifying the Congo Theme to Support Submenus #

Unfortunately, the default Congo theme doesn’t render submenu items out of the box. But Hugo’s templating system gives us full control.

Override the Theme’s Header Template #

Copy the theme’s basic.html header partial to your project:

mkdir -p layouts/partials/header/
cp themes/congo/layouts/partials/header/basic.html layouts/partials/header/basic.html

Open the file and find the main menu list:

<ul class="flex list-none flex-col text-end sm:flex-row">  
    {{ if .Site.Menus.main }}  
    {{ range .Site.Menus.main }}  
    ...
    <li class="group mb-1 sm:mb-0 sm:me-7 sm:last:me-0.5">
		...
		<!-- ADD YOUR CODE HERE -->
	</li>
	{{ end }}
	...
	{{ end }}
</ul>

Instead of marked comment add following code:

{{ if .HasChildren }}
<ul>
    {{ range .Children }}
    <li><a href="{{ .URL }}">{{ .Name }}</a></li>
    {{ end }}
</ul>
{{ end }}

This ensures submenu items will be rendered if defined.

Styling the Dropdown Menu with CSS #

To make submenus visually functional, you’ll need to add some CSS.

If it doesn’t exist yet, create this file:

assets/css/custom.css

Then paste the following styles:

nav ul {  
    list-style: none;  
    padding: 0;  
    margin: 0;  
    position: relative;  
}  
  
nav li {  
    position: relative;  
}  
  
/* Top-level menu link styling */  
nav > ul > li {  
    display: inline-block;  
    padding: 10px 15px;  
    position: relative;  
}  
  
/* Add dropdown arrow for items with submenus */  
nav li > ul::before {  
    content: "";  
    display: none;  
}  
  
nav li:has(ul) > a::after {  
    content: "▼";  
    font-size: 0.6em;  
    margin-left: 5px;  
    position: absolute;  
}  
  
/* Submenu styles */  
nav ul ul {  
    display: none;  
    position: absolute;  
    top: 100%;  
    left: 0;  
    background-color: rgba(245, 245, 245, 0.95);  
    border-radius: 15px;  
    list-style: none;  
    padding: 5px 5px;  
    margin: 0 0 0 -10px;  
    min-width: 160px;  
    z-index: 1000;  
}  
  
/* Show submenu on hover */  
nav li:hover > ul {  
    display: block;  
}  
  
/* Submenu item styling */  
nav ul ul li {  
    padding: 8px 15px;  
    white-space: nowrap;  
    text-align: left;  
}  
  
/* Submenu link styling */  
nav ul ul li a {  
    text-decoration: none;  
    color: #333;  
    display: block;  
}  
  
/* Hover effect on submenu items */  
nav ul ul li:hover {  
    text-decoration: underline;  
    text-underline-offset: 2px;  
    text-decoration-thickness: 2px;  
    text-decoration-color: rgba(var(--color-primary-500), 1);  
}

You can customize the CSS as you wish — and just like that, your menu now supports subitems.


That’s It! #

Now you have:

  • Custom taxonomies
  • A clear content structure
  • A menu with working submenus
  • Minimal CSS to make it look good

This setup gives you full control over how your blog is organized. It’s also easy to expand with more taxonomies or menu levels as your content grows.